function data_out = WiSiL_Tx_hpAmplifier(data_in, rfImperfections)

% hpAmplifier implements non linear amplifier in signal transmission
% 
% Syntax:
% data_out = hpAmplifier(data_in, amplifier)
%
%   amplifier.model = 'none'
%   In this case, it is not used any amplifier model.
%
%   amplifier.model = 'hard limiter'
%   This model is an idealized amplifier. It is linear until the saturation
%   level, where it simply clips the signal. The saturation level is set to
%   1.
%              
%   amplifier.model = 'rapp'
%   This is used to model a solid state power amplifier. It is basically an
%   amplitude (AM/AM) distortion, since the AM/PM distortion is null. In this
%   implementation, the output saturation level and the small signal gain are
%   set to 1. The input paramenter p is the smothness factor.
%
%   amplifier.model = 'saleh'
%   This is a implementation of a general Travelling Wave Tube(TWT) amplifier.
%   The parameters alphaG and betaG are responsible to the AM/AM conversion
%   and alphaPhi and betaPhi, to the AM/PM conversion. It fallows the Saleh
%   model.
%
% References:
%           - Effects of HPA-Nonlinearity on a 4-DPSK/OFDM Signal for a 
% Digital Sound Broadcasting System
%           - Multicarrier communication systems with low sensitivity to
% nonlinear amplification. page 24
%           - Frequency-Independent and Frequency-Dependent Nonlinear
% Models of TWT Amplifiers
%

% Created by Ian Ulian - 16/04/2009 


if (any(strcmpi(rfImperfections.amplifier.model,{'none', 'hard limiter', ...
        'rapp', 'saleh'}))==0)
    error( 'rfImperfections.amplifier.model must be ''none'', ''hard limiter'', ''rapp'' or ''saleh'' ');
end

if findstr(lower(rfImperfections.amplifier.model), 'none')

   data_out = data_in;
   
end

if findstr(lower(rfImperfections.amplifier.model), 'hard limiter')
    IBO = rfImperfections.amplifier.IBO;
    sigma2 = mean(abs(data_in).*abs(data_in));
    max = sqrt(sigma2) * 10^(IBO/20);

    A_in = abs(data_in);
    theta = angle(data_in);
    
    aux = find(A_in > max);
    theta_aux = theta(aux);

    data_out = data_in;
    data_out(aux) = max .* cos(theta_aux) + i * max .* sin(theta_aux); 
    IBOeficaz = 20*log10(max/sqrt(sigma2));

end

if findstr(lower(rfImperfections.amplifier.model), 'rapp')
    if rfImperfections.amplifier.p < 2 || rfImperfections.amplifier.p > 3
    error('rfImperfections.amplifier.p value must be between 2 and 3')
    end
    p = rfImperfections.amplifier.p;
    sat = rfImperfections.amplifier.saturation;
    IBO = rfImperfections.amplifier.IBO;
    
    sigma2 = mean(abs(data_in).*abs(data_in));
    Pin = sat^2 / 10^(IBO/10);
    fp = sigma2/Pin;
    
    data_in = data_in / sqrt(fp);
    
    den = (1 + abs(data_in).^(2*p)).^(1/(2*p));
    data_out = data_in./den;
        IBOeficaz = 20*log10(sat/sqrt(mean(abs(data_in).*abs(data_in))));
end

if findstr(lower(rfImperfections.amplifier.model), 'saleh')
    
    alphaG = rfImperfections.amplifier.alphaG;
    betaG = rfImperfections.amplifier.betaG;
    alphaPhi = rfImperfections.amplifier.alphaPhi;
    betaPhi = rfImperfections.amplifier.betaPhi;
    
    A_in = abs(data_in);

    A_out = (alphaG * A_in)./(1 + betaG * A_in .* A_in);

    theta_in = angle(data_in);
    theta_dist = (alphaPhi * A_in .* A_in)./(1 + betaPhi * A_in .* A_in);
    theta_out = theta_in + theta_dist;

    data_out = A_out .* cos(theta_out) + i * A_out .* sin(theta_out);

end



